# -*- coding: utf-8 -*-

from .ir_var import IVar
from .ir_value import IValue
from .ir_type import IType
from .ir_ds import ITypeList


INDENT_SPACE = '  '

def str_indent(raw_dump: str, level: int = 1):
    raw_line = raw_dump.split('\n')
    return '\n'.join((INDENT_SPACE * level + x) for x in raw_line)



class IExp:
    def __init__(self, type: IType):
        self.type = type


class IExpValue(IExp):
    def __init__(self, value: IValue):
        super(IExpValue, self).__init__(value.type)
        self.value = value

    def __str__(self):
        return str(self.value)


class IExpSet(IExp):
    def __init__(self, data: list):
        super(IExpSet, self).__init__(IType('Set'))
        self.data = data

    def __str__(self):
        return '({})'.format(', '.join(str(x) for x in self.data))


class IExpSubscript(IExp):
    def __init__(self, parent, index, type: IType):
        super(IExpSubscript, self).__init__(type)
        self.parent = parent
        self.index = index

    def __str__(self):
        return '{}[{}]'.format(self.parent, self.index)


class IExpMember(IExp):
    def __init__(self, parent, attr, type: IType, value=None):
        super(IExpMember, self).__init__(type)
        self.parent = parent
        self.attr = attr
        self.value = value

    def __str__(self):
        return '{}.{}'.format(self.parent, self.attr)


class IExpFunc(IExp):
    def __init__(self, func_name: str, parent, funcdef):
        super(IExpFunc, self).__init__(IType('ExpFunc'))
        self.func_name = func_name
        self.parent = parent
        self.ret_type, self.params = funcdef

    def __str__(self):
        if self.parent is None:
            prefix = ''
        else:
            prefix = '{}.'.format(self.parent)
        return '{}{}'.format(prefix, self.func_name)


class IExpUnary(IExp):
    USUB = '-'
    NOT = '!'

    def __init__(self, operand, op, type: IType):
        super(IExpUnary, self).__init__(type)
        self.operand = operand
        self.op = op

    def __str__(self):
        return '({})'.format(str(self.op) + str(self.operand))


class IExpBinary(IExp):
    ADD = '+'
    SUB = '-'
    MULT = '*'
    DIV = '/'
    MOD = '%'

    OR = '||'
    AND = '&&'
    EQUAL = '=='
    NOTEQUAL = '!='
    GREATER = '>'
    LESSER = '<'

    BOR = '|'
    BAND = '&'
    BXOR = '^'
    BLSHIFT = '<<'
    BRSHIFT = '>>'

    def __init__(self, left, op, right, type: IType):
        super(IExpBinary, self).__init__(type)
        self.left = left
        self.op = op
        self.right = right

    def __str__(self):
        return '({} {} {})'.format(str(self.left), str(self.op), str(self.right))


class IOpAssign:
    def __init__(self, variable: IVar, exp: IExp):
        self.target = variable
        self.exp = exp

    def __str__(self):
        return '{} = {}'.format(self.target, self.exp)


class IOpAugAssign:
    ADD = '+'
    SUB = '-'
    MULT = '*'
    DIV = '/'
    MOD = '%'

    BOR = '|'
    BAND = '&'
    BXOR = '^'
    BLSHIFT = '<<'
    BRSHIFT = '>>'

    def __init__(self, variable: IVar, exp: IExp, op):
        self.target = variable
        self.exp = exp
        self.op = op

    def __str__(self):
        return '{} {}= {}'.format(self.target, self.op, self.exp)


class IOpCall:
    def __init__(self, funcdef: IExpFunc, params: list):
        self.funcdef = funcdef
        self.params = params

    def __str__(self):
        param_data = ', '.join(str(x) for x in self.params)
        return '{}({})'.format(self.funcdef, param_data)


class IOpReturn:
    def __init__(self):
        pass

    def __str__(self):
        return 'Return'


class IOpRange:
    def __init__(self, start, end, step):
        self.start = start
        self.end = end
        self.step = step

    def __str__(self):
        return 'Range({}->{}: {})'.format(self.start, self.end, self.step)


class IOpBody:
    def __init__(self, ops: list):
        self.ops = ops

    def __str__(self):
        return '\n'.join(str(x) for x in self.ops)


class IOpIf:
    def __init__(self, condition, body: IOpBody, elbody: IOpBody = None):
        self.condition = condition
        self.body = body
        self.elbody = elbody

    def __str__(self):
        if self.elbody is None:
            else_data = ''
        else:
            else_data = '\nElse:\n' + str_indent(str(self.elbody))
        # Convert the condition.
        condition_str = str(self.condition)
        if condition_str[0] != '(':
            condition_str = '({})'.format(condition_str)
        return 'If {}:\n{}{}'.format(condition_str, str_indent(str(self.body)),
                                     else_data)


class IOpIfExp:
    def __init__(self, condition, body, elbody):
        self.condition = condition
        self.body = body
        self.elbody = elbody

    def __str__(self):
        return '{} ? {} : {}'.format(self.condition, self.body, self.elbody)


class IOpPass:
    def __init__(self):
        pass

    def __str__(self):
        return 'pass'


class IOpBreak:
    def __init__(self):
        pass

    def __str__(self):
        return 'break'


class IOpContinue:
    def __init__(self):
        pass

    def __str__(self):
        return 'continue'


class IOpFor:
    def __init__(self, iter_var: IVar, iter_range: IOpRange, body: IOpBody):
        self.iter_var = iter_var
        self.iter_range = iter_range
        self.body = body

    def __str__(self):
        return 'For {}: {}\n{}'.format(self.iter_var, self.iter_range, str_indent(str(self.body)))


class IOpLen:
    def __init__(self, var: IVar):
        self.var = var

    def __str__(self):
        return 'len({})'.format(self.var)


class IOpTypeConvert:
    def __init__(self, var: IVar, type: IType):
        self.var = var
        self.type = type

    def __str__(self):
        return '{}({})'.format(self.type, self.var)


class IOpConstruct:
    def __init__(self, type: IType):
        self.type = type

    def __str__(self):
        return '{}()'.format(self.type)


class IOpContain:
    def __init__(self, var, item):
        self.var = var
        self.item = item

    def __str__(self):
        return 'contain({}, {})'.format(self.var, self.item)


class IValueListComp(IValue):
    def __init__(self, managed_type: IType, exp: IExp, iter_range: IOpRange, iter_var, iter_init):
        super(IValueListComp, self).__init__(ITypeList(managed_type), [])
        self.exp = exp
        self.iter_var = iter_var
        self.iter_range = iter_range
        self.iter_init = iter_init

    def __str__(self):
        return '[{}, ...]'.format(self.exp)


class IExpVoid:
    def __init__(self):
        pass

    def __str__(self):
        return '_'